From 9bb46b0994e498d09460d44f09fbcd0021ced0db Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Sun, 9 Aug 2026 11:05:54 +0200 Subject: [PATCH] Backport of pcre2-10.48-Check-JIT-mode-before-validation.patch Cherry-pick of f67db227af31bba7cdf2a7a00b97af91b588c2f5 and e2f100156b94cf85dce9046b47a3274590487de4 Fix pcre2_match to check for JIT support before JIT validation & execution (#926) This fixes the issue that the JIT branch's UTF validation is not pinned to be identical to the interpreter's validation. This was not robust, and lead to a bug, in the case where the JIT UTF validation is done, but because the relevant JIT mode was not compiled, it falls through to the interpreter and skips the interpreter's own UTF validation and setup. Add a testcase for #926 (#940) (cherry picked from commit ab7097fbde778e0e4d17752ba2a14a7430d35385) --- src/pcre2_internal.h | 2 ++ src/pcre2_jit_match.c | 1 + src/pcre2_jit_misc.c | 26 +++++++++++++++++++ src/pcre2_match.c | 59 ++++++++++++++----------------------------- testdata/testinput17 | 8 ++++++ testdata/testoutput17 | 8 ++++++ 6 files changed, 64 insertions(+), 40 deletions(-) diff --git a/src/pcre2_internal.h b/src/pcre2_internal.h index a22e8b1..3b570ea 100644 --- a/src/pcre2_internal.h +++ b/src/pcre2_internal.h @@ -2175,6 +2175,7 @@ is available. */ #define _pcre2_is_newline PCRE2_SUFFIX(_pcre2_is_newline_) #define _pcre2_jit_free_rodata PCRE2_SUFFIX(_pcre2_jit_free_rodata_) #define _pcre2_jit_free PCRE2_SUFFIX(_pcre2_jit_free_) +#define _pcre2_jit_check_exec PCRE2_SUFFIX(_pcre2_jit_check_exec_) #define _pcre2_jit_get_size PCRE2_SUFFIX(_pcre2_jit_get_size_) #define _pcre2_jit_get_target PCRE2_SUFFIX(_pcre2_jit_get_target_) #define _pcre2_memctl_malloc PCRE2_SUFFIX(_pcre2_memctl_malloc_) @@ -2203,6 +2204,7 @@ extern BOOL _pcre2_is_newline(PCRE2_SPTR, uint32_t, PCRE2_SPTR, uint32_t *, BOOL); extern void _pcre2_jit_free_rodata(void *, void *); extern void _pcre2_jit_free(void *, pcre2_memctl *); +extern BOOL _pcre2_jit_check_exec(void *, uint32_t); extern size_t _pcre2_jit_get_size(void *); const char * _pcre2_jit_get_target(void); extern void * _pcre2_memctl_malloc(size_t, pcre2_memctl *); diff --git a/src/pcre2_jit_match.c b/src/pcre2_jit_match.c index 23a2d40..6afc6d9 100644 --- a/src/pcre2_jit_match.c +++ b/src/pcre2_jit_match.c @@ -118,6 +118,7 @@ jit_arguments arguments; int rc; int index = 0; +/* The same check is performed by jit_check_exec(). */ if ((options & PCRE2_PARTIAL_HARD) != 0) index = 2; else if ((options & PCRE2_PARTIAL_SOFT) != 0) diff --git a/src/pcre2_jit_misc.c b/src/pcre2_jit_misc.c index c3abc0b..2045232 100644 --- a/src/pcre2_jit_misc.c +++ b/src/pcre2_jit_misc.c @@ -214,6 +214,32 @@ return sljit_get_platform_name(); } +/************************************************* +* Checks function compilation * +*************************************************/ + +BOOL +PRIV(jit_check_exec)(void *executable_jit, uint32_t options) +{ +#ifndef SUPPORT_JIT +(void)executable_jit; +(void)options; +return FALSE; +#else /* SUPPORT_JIT */ +/* The same check is performed at the beginning of pcre2_jit_match(). */ +executable_functions *functions = (executable_functions *)executable_jit; +int index = 0; + +if ((options & PCRE2_PARTIAL_HARD) != 0) + index = 2; +else if ((options & PCRE2_PARTIAL_SOFT) != 0) + index = 1; + +return functions->executable_funcs[index] != NULL; +#endif +} + + /************************************************* * Get size of JIT code * *************************************************/ diff --git a/src/pcre2_match.c b/src/pcre2_match.c index 34a92ea..faedff7 100644 --- a/src/pcre2_match.c +++ b/src/pcre2_match.c @@ -6867,10 +6867,6 @@ PCRE2_SPTR req_cu_ptr; PCRE2_SPTR start_partial; PCRE2_SPTR match_partial; -#ifdef SUPPORT_JIT -BOOL use_jit; -#endif - /* This flag is needed even when Unicode is not supported for convenience (it is used by the IS_NEWLINE macro). */ @@ -6880,9 +6876,6 @@ BOOL utf = FALSE; BOOL ucp = FALSE; BOOL allow_invalid; uint32_t fragment_options = 0; -#ifdef SUPPORT_JIT -BOOL jit_checked_utf = FALSE; -#endif #endif /* SUPPORT_UNICODE */ PCRE2_SIZE frame_size; @@ -6943,15 +6936,6 @@ options |= (re->flags & FF) / ((FF & (~FF+1)) / (OO & (~OO+1))); #undef FF #undef OO -/* If the pattern was successfully studied with JIT support, we will run the -JIT executable instead of the rest of this function. Most options must be set -at compile time for the JIT code to be usable. */ - -#ifdef SUPPORT_JIT -use_jit = (re->executable_jit != NULL && - (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0); -#endif - /* Initialize UTF/UCP parameters. */ #ifdef SUPPORT_UNICODE @@ -6997,20 +6981,25 @@ match_data->startchar = 0; /* ============================= JIT matching ============================== */ -/* Prepare for JIT matching. Check a UTF string for validity unless no check is -requested or invalid UTF can be handled. We check only the portion of the -subject that might be be inspected during matching - from the offset minus the -maximum lookbehind to the given length. This saves time when a small part of a -large subject is being matched by the use of a starting offset. Note that the -maximum lookbehind is a number of characters, not code units. */ +/* If the pattern was successfully studied with JIT support, we will run the +JIT executable instead of the rest of this function. Most options must be set +at compile time for the JIT code to be usable. */ #ifdef SUPPORT_JIT -if (use_jit) +if (re->executable_jit != NULL && + (options & ~PUBLIC_JIT_MATCH_OPTIONS) == 0 && + PRIV(jit_check_exec)(re->executable_jit, options)) { + /* Prepare for JIT matching. Check a UTF string for validity unless no check + is requested or invalid UTF can be handled. We check only the portion of the + subject that might be be inspected during matching - from the offset minus + the maximum lookbehind to the given length. This saves time when a small part + of a large subject is being matched by the use of a starting offset. Note that + the maximum lookbehind is a number of characters, not code units. */ + #ifdef SUPPORT_UNICODE if (utf && (options & PCRE2_NO_UTF_CHECK) == 0 && !allow_invalid) { - /* For 8-bit and 16-bit UTF, check that the first code unit is a valid character start. */ @@ -7063,16 +7052,14 @@ if (use_jit) match_data->startchar += start_match - subject; return match_data->rc; } - jit_checked_utf = TRUE; } #endif /* SUPPORT_UNICODE */ - /* If JIT returns BADOPTION, which means that the selected complete or - partial matching mode was not compiled, fall through to the interpreter. */ - rc = pcre2_jit_match(code, subject, length, start_offset, options, match_data, mcontext); - if (rc != PCRE2_ERROR_JIT_BADOPTION) + /* JIT must be able to perform the match. */ + PCRE2_ASSERT(rc != PCRE2_ERROR_JIT_BADOPTION); + { match_data->subject_length = length; if (rc >= 0 && (options & PCRE2_COPY_MATCHED_SUBJECT) != 0) @@ -7098,12 +7085,8 @@ this. */ mb->check_subject = subject; -/* If a UTF subject string was not checked for validity in the JIT code above, -check it here, and handle support for invalid UTF strings. The check above -happens only when invalid UTF is not supported and PCRE2_NO_CHECK_UTF is unset. -If we get here in those circumstances, it means the subject string is valid, -but for some reason JIT matching was not successful. There is no need to check -the subject again. +/* Check the validity of UTF subject strings. The check happens only when +PCRE2_NO_CHECK_UTF is unset. We check only the portion of the subject that might be be inspected during matching - from the offset minus the maximum lookbehind to the given length. @@ -7115,11 +7098,7 @@ Note also that support for invalid UTF forces a check, overriding the setting of PCRE2_NO_CHECK_UTF. */ #ifdef SUPPORT_UNICODE -if (utf && -#ifdef SUPPORT_JIT - !jit_checked_utf && -#endif - ((options & PCRE2_NO_UTF_CHECK) == 0 || allow_invalid)) +if (utf && ((options & PCRE2_NO_UTF_CHECK) == 0 || allow_invalid)) { #if PCRE2_CODE_UNIT_WIDTH != 32 BOOL skipped_bad_start = FALSE; diff --git a/testdata/testinput17 b/testdata/testinput17 index b979a63..c396e8e 100644 --- a/testdata/testinput17 +++ b/testdata/testinput17 @@ -318,4 +318,12 @@ /(...)-(...)/jitfast abc-xyz\=get=2 +# Commented-out test; please re-enable and run manually on Unicode builds +# #if unicode +# +# /\b/B,utf,ucp,jit=1 +# \xff\x00\x00\=offset=2,ps +# +# #endif + # End of testinput17 diff --git a/testdata/testoutput17 b/testdata/testoutput17 index 2f1c4e9..e7334e4 100644 --- a/testdata/testoutput17 +++ b/testdata/testoutput17 @@ -575,4 +575,12 @@ Failed: error -47: match limit exceeded 2: xyz 2G xyz (3) +# Commented-out test; please re-enable and run manually on Unicode builds +# #if unicode +# +# /\b/B,utf,ucp,jit=1 +# \xff\x00\x00\=offset=2,ps +# +# #endif + # End of testinput17 -- 2.39.5